//#define CXXOMFORT_NOTICES 1
#include <cxxomfort/cxxomfort.hpp>
#include <cxxomfort/tuple.hpp>
#include <cxxomfort/library/foreach.hpp>
#include <cxxomfort/library/tuple.hpp>
#include <cxxomfort/library/operatorit.hpp>
#include <cxxomfort/functional.hpp> // ptr_fun, mem_fn
#include <iostream>
#include <ctime>
#include <cstdio> // printf
#include <cmath>
#include <string> // string::substr
#include <vector>

#if (!defined(__cpp_lib_apply))
	#error "std::apply not detected or implemented"
#endif
//
// booh
//

struct booh {
    booh() : _v(rand()) {}
    int value () const { return _v; }
    booh& operator++() {
        std::cout<< "[booh++!]"<< std::endl;
        return *this;
    }
    int _v;
};

//
// wrapper that gives a std::string a ++ operator
//
struct ppstring 
: std::string {
    typedef std::string base_type;
    ppstring (std::string const& s) : std::string(s) {}
    ppstring& operator++ () {
        static_cast<base_type*>(this)->append("!");
        return *this;
    }
};


std::ostream& operator<< (std::ostream& os, booh const& b) {
    return os<< b.value();
}

static const char* m [] = {"Hello World", "0123456789abcdef", "H€llo", "Hellø", "Bye~"};

inline std::string string_substr (std::string const& S, size_t from, size_t count) {
    return S.substr(from,count);
}

typedef std::tuple<float,ppstring,int,booh> TupleT;

// a functor tha operates on the components of a TupleT

struct DuplT {
    typedef void result_type;
    void operator() (float& f, ppstring& p, int& i, booh& b) {
        ++f; 
        p+= p;
        i-= 10;
        (void)b;
    }
    
} dupfn = {};


int main () {
    using namespace std;
    srand(time(0));
    
    TupleT tt (-2.0727f, string("Hello World!"), -1, booh());
    cout<< "Values of tuple: "<< endl;
    cout<< "float: "<< get<float>(tt)<< ", string: "<< get<ppstring>(tt)<< ", int: "<< get<int>(tt)<< ", booh: "<< get<booh>(tt)<< endl;
    cout<< "apply: "<< endl;
    //cxxomfort::functional::preincrement<void> p;
    cout<< "We'll pass now the tuple as arguments to 'dupfn' \n "
    << "which performs the following tasks: \n"
    << " * decrements each numeric argument\n"
    << " * duplicates each string argument\n"
    << " * leaves booh alone (booh...)\n"
    << endl;
    apply( dupfn, tt);
    cout<< "float: "<< get<float>(tt)<< ", string: "<< get<ppstring>(tt)<< ", int: "<< get<int>(tt)<< ", booh: "<< get<booh>(tt)<< endl;
    
    
}

